#include "heroz.hh"
#include "heroz_screens.hh"
#include <cstdio>
#include <cstring>
#include <iostream>
#include <stdexcept>

using namespace std;

std::vector<ScreenCharacters> &new_screen(std::vector<ScreenCharacters> &screens) {
  ScreenCharacters scr;
  int i;

  for(i = 0; i < SCREENHEIGHT; ++i) scr.s[i][SCREENWIDTH] = '\n';
  screens.push_back(scr);
  return screens;
}

void ScreenCharacters::clear(unsigned char c) {
  int x, y;

  for(x = 0; x < SCREENWIDTH; ++x) 
    for(y = 0; y < SCREENHEIGHT; ++y)
      s[y][x] = c;
}

void ScreenCharacters::set(unsigned int x, unsigned int y, char c) {
#ifndef NDEBUG
  if(x >= SCREENWIDTH) throw std::invalid_argument("x >= SCREENWIDTH");
  if(y >= SCREENHEIGHT) throw std::invalid_argument("y >= SCREENHEIGHT");
#endif
  s[y][x] = c;
}

unsigned char ScreenCharacters::get(unsigned int x, unsigned int y) {
#ifndef NDEBUG
  if(x >= SCREENWIDTH) throw std::invalid_argument("x >= SCREENWIDTH");
  if(y >= SCREENHEIGHT) throw std::invalid_argument("y >= SCREENHEIGHT");
#endif
  return s[y][x];
}

unsigned char ScreenCharacters::get_save(unsigned int x, unsigned int y, unsigned char c) {
  if(x >= SCREENWIDTH || y >= SCREENHEIGHT) return c;
  return s[y][x];
}

void ScreenCharacters::shift_left() {
  int x, y;

  for(y = 0; y < SCREENHEIGHT; ++y) {
    unsigned char ch = get(0, y);
    for(x = 1; x < SCREENWIDTH; ++x) {
      set(x - 1, y, get(x, y));
    }
    set(SCREENWIDTH - 1, y, ch);
  }
}

void ScreenCharacters::shift_right() {
  int x, y;

  for(y = 0; y < SCREENHEIGHT; ++y) {
    unsigned char ch = get(SCREENWIDTH - 1, y);
    for(x = SCREENWIDTH - 1; x >= 1; --x) {
      set(x, y, get(x - 1, y));
    }
    set(0, y, ch);
  }
}

void ScreenCharacters::shift_up() {
  int x, y;

  for(x = 0; x < SCREENWIDTH; ++x) {
    unsigned char ch = get(x, 0);
    for(y = 1; y < SCREENHEIGHT; ++y)  {
      set(x, y - 1, get(x, y));
    }
    set(x, SCREENHEIGHT - 1, ch);
  }
}

void ScreenCharacters::shift_down() {
  int x, y;

  for(x = 0; x < SCREENWIDTH; ++x) {
    unsigned char ch = get(x, SCREENHEIGHT - 1);
    for(y = SCREENHEIGHT - 1; y >= 1; --y)  {
      set(x, y, get(x, y - 1));
    }
    set(x, 0, ch);
  }
}


void save_screens(std::string path, const std::vector<ScreenCharacters> &screens) {
  FILE *out;
  char buf[512];

  if(screens.size() > 0xFFFD) throw std::invalid_argument("too many screens");
  for(unsigned int i = 0; i < screens.size(); ++i) {
    sprintf(buf, "%s/screen.%04x", path.c_str(), i);
    out = fopen(buf, "w");
    if(!out) throw std::runtime_error(strerror(errno));
    fwrite(&screens[i].s, 1, sizeof(ScreenCharacters::s), out);
    fclose(out);
  }
}

std::vector<ScreenCharacters> load_screens(std::string path) {
  int i;
  int bytes_read;
  FILE *in;
  char buf[16];
  ScreenCharacters screen;
  string tmpname;
  std::vector<ScreenCharacters> screens;

  path += "/screen.";
  for(i = 0; i < 63; ++i) {
    sprintf(buf, "%04x", i);
    tmpname = path + buf;
    if((in = fopen(tmpname.c_str(), "r")) == NULL) {
      if(ENOENT != errno) throw std::runtime_error(strerror(errno));
      break;
    }
    bytes_read = fread(&screen.s, 1, sizeof(screen.s), in);
#ifdef DEBUG
    printf("$%04X bytes read from '%s'.\n", bytes_read, tmpname.c_str());
#endif
    if(ferror(in) != 0) throw std::runtime_error(strerror(errno));
    fclose(in);
    screens.push_back(screen);
  }
  return screens;
}

void copy_screen(int screen, HeroZ *heroz) {
  *heroz->screen = heroz->screens.at(screen);
}

void draw_screen(HeroZ *heroz) {
  SDL_Rect srect, drect;
  Uint32 ticks = SDL_GetTicks();
  int tilesize = heroz->font->txtfont->w;

  draw_screen(heroz->screen_buffer, heroz->font->gfxfont, ticks / 171, heroz->screen);
  srect.x = 0;
  srect.w = tilesize;
  srect.h = tilesize;
  for(int y = 0; y < SCREENHEIGHT; ++y) {
    for(int x = 0; x < SCREENWIDTH; ++x) {
      int tilenum = heroz->txtscreen->s[y][x];
      if(tilenum != ' ') {
	srect.y = tilenum * tilesize;
	drect.x = x * tilesize;
	drect.y = y * tilesize;
	SDL_BlitSurface(heroz->font->txtfont, &srect, heroz->screen_buffer, &drect);
      }
    }
  }
}

void draw_screen(SDL_Surface *display, SDL_Surface *gfxfont, int asn, const ScreenCharacters *screen) {
  SDL_Rect srect, drect;
  
#if DEBUG > 2
  for(int y = 0; y < SCREENHEIGHT; ++y) {
    for(int x = 0; x < SCREENWIDTH; ++x) {
      printf("%02X ", screen->s[y][x]);
    }
    putchar('\n');
  }
#endif
  srect.x = (asn % (gfxfont->w >> TILEWSHIFT)) << TILEWSHIFT;
  srect.w = TILEWIDTH;
  srect.h = TILEHEIGHT;
  for(int y = 0; y < SCREENHEIGHT; ++y) {
    for(int x = 0; x < SCREENWIDTH; ++x) {
      int tilenum = screen->s[y][x];
      srect.y = tilenum * TILEHEIGHT;
      drect.x = x << TILEWSHIFT;
      drect.y = y << TILEHSHIFT;
      SDL_BlitSurface(gfxfont, &srect, display, &drect);
    }
  }
}
